# -*- coding: utf-8 -*-
#
#    Copyright (C) 2012 Gonzalo Bonigo <gonzalobonigo@gmail.com>
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see http://www.gnu.org/licenses/
#


"""
    En este modulo se define la clase Alphabet, la cual reprecenta un alfabeto
    de un LTS.
"""

__version__ = 3.1

from error import LTSError

class Alphabet:
    """
        Esta clase representa el alfabeto de un LTS.
    
        Un alfabeto es un conjunto de eventos. Los eventos estan representados
        por cadenas de caracteres (strings), y se dividen en cuatro tipos:
        observables, no observables, de falla y de sincronizacion. Para
        diferenciar a que tipo pertence cada evento, se exige que estos
        comienzen con un caracter en particular; los eventos observables
        deben comenzar con el caracter 'o', lo no observables con el caracter
        'u', los de falla con 'f', y los de sincronizacion con 'c'.
    
        Para los metodos de esta clase, los posibles valores del argumento
        'event_type' son:
            'o'    =>    Conjutno de eventos observables.
            'u'    =>    Conjunto de eventos no observables.
            'f'    =>    Conjunto de eventos de falla.
            'c'    =>    Conjunto de eventos de sincronizacion.
        En caso de proporcionar un valor incorrecto se produce una excepcion
        de tipo LTSError.
    """
    
    def __init__ (self):
        self.sets = dict()
        # Conjunto de eventos observables.
        self.sets['o'] = set()
        # Conjunto de eventos no observables.
        self.sets['u'] = set()
        # Conjunto de eventos de falla.
        self.sets['f'] = set()
        # Conjunto de eventos de sincronizacion.
        self.sets['c'] = set()
    
    def __copy__ (self):
        result = Alphabet()
        result.sets['o'] = set(self.sets['o'])
        result.sets['u'] = set(self.sets['u'])
        result.sets['f'] = set(self.sets['f'])
        result.sets['c'] = set(self.sets['c'])
        return result
    
    def is_event (self, event, event_type):
        """
            Descripcion:
                Dice si el evento 'event' existe en el conjunto de eventos
                indicado por 'event_type'.
            Arguemtnos:
                event      - Una cadena de caracteres que repcenta un evento.
                event_type - Un caracter que especifica a que tipo de evento
                             pertenece. Sus posibles valores son: o|u|f|c.
            Retorno:
                True  - Si el evento pertence al alfabeto.
                Falso - Si no.
        """
        try:
            return event in self.sets[event_type]
        except KeyError:
            raise LTSError("Incorrect type event:", event_type)
    
    def add_event (self, event, event_type):
        """
            Descripcion:
                Agrega el evento 'event' al conjunto de eventos indicado por
                'event_type'.
            Arguemtnos:
                event      - Una cadena de caracteres que repcenta un evento.
                event_type - Un caracter que especifica a que tipo de evento
                             pertenece. Sus posibles valores son: o|u|f|c.
            Retorno:
                No tiene.
        """
        try:
            (self.sets[event_type]).add(event)
        except KeyError:
            raise LTSError("Incorrect type event:", event_type)
    
    def remove_event (self, event, event_type):
        """
            Descripcion:
                Elimina el evento 'event' del conjunto de eventos indicado por
                'event_type'.
            Arguemtnos:
                event      - Una cadena de caracteres que repcenta un evento.
                event_type - Un caracter que especifica a que tipo de evento
                             pertenece. Sus posibles valores son: o|u|f|c.
            Retorno:
                No tiene.
        """
        try:
            (self.sets[event_type]).remove(event)
        except KeyError:
            raise LTSError("Incorrect type event", event_type)
    
    def get_event_set (self, event_type):
        """
            Descripcion:
                Devuelve el conjunto de eventos indicado por 'event_type'.
                NOTA: Se devuelve una referencia al conjunto, no una copia.
                IMPORTANTE: Se recomienda no hacer modificaciones al conjunto
                a traves de esta referencia.
            Arguemtnos:
                event_type - Un caracter que especifica el tipo de evento.
                             Sus posibles valores son: o|u|f|c.
            Retorno:
                Devuelve una instancia de la clase Set() la cual tiene el
                conjunto de eventos de tipo "event_type" del alfabeto.
        """
        try:
            return self.sets[event_type]
        except KeyError:
            raise LTSError("Incorrect type event", event_type)
    
    def union (self, alphabet):
        """
            Descripcion:
                Une dos alfabaetos.
                Crea y devuelve un nuevo alfabeto el cual es el resultado de la
                union de los alfabetos 'self' y 'alphabet'.
            Arguemtnos:
                alphabet - Una instancia de la clase Alphabet().
            Retorno:
                Devuelve una nueva instancia de la clase Alphabet() la cual
                contiene la union de los eventos de los dos alfabetos.
        """
        result = Alphabet()
        result.sets['o'] = self.sets['o'].union(alphabet.sets['o'])
        result.sets['u'] = self.sets['u'].union(alphabet.sets['u'])
        result.sets['f'] = self.sets['f'].union(alphabet.sets['f'])
        result.sets['c'] = self.sets['c'].union(alphabet.sets['c'])
        return result
